package com.sefer.dragonfly.client.console.servlet;

import java.io.IOException;
import java.io.PrintWriter;
import java.util.Iterator;
import java.util.Set;

import javax.management.Attribute;
import javax.management.MBeanAttributeInfo;
import javax.management.MBeanInfo;
import javax.management.MBeanOperationInfo;
import javax.management.MBeanServer;
import javax.management.ObjectName;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;

import org.apache.log4j.Logger;

import com.sefer.dragonfly.client.utils.CommonUtils;
import com.sefer.dragonfly.client.utils.MBeanAnnotationRegister;

/**
 * <pre>
 * jmx Filter 代理，通过该filter来访问和管理mbean
 * 关键词：  qry , set ,get, att , val , opr, mname , params
 * 操作包括以下三种：  
 * 
 *   1. 查询mbean: 
 *   http://xxxx?qry=objectname   
 *   objectname 为 mbean注册的名称，查找所有mbean可以填写"*:*"，或者不填.   
 * 
 *   2. 操作属性：
 *   http://xxxx?set=objectname&att=****val=***
 *   http://xxxx?get=objectname&att=****
 *   att为属性的名称，val为需要设置的属性的值
 *   
 *    
 *   3. 操作方法： (暂时不支持带有参数的方法)
 *   http://xxxx?opr=objectname&mname=***
 *   mname为方法名称
 *   
 *   4. 基于JSONP操作方法： (暂时不支持带有参数的方法)，解决跨域问题
 *   http://xxxx?jopr=objectname&mname=***
 *   mname为方法名称
 * 
 *   如果同时出现以上查询set ,get ,opr, qry 的关键词，以set,get,opr, qry的优先级顺序执行.
 * </pre>
 * 
 * @author xiaofeng 2011-9-5 上午10:55:57
 */
public class JMXProxyFilter implements Filter {

	private static Logger logger = Logger.getLogger(JMXProxyFilter.class);

	private static final long serialVersionUID = 1L;
	/**
	 * MBean server.
	 */
	protected MBeanServer mBeanServer = null;

	public void getAttribute(PrintWriter writer, String onameStr, String att) {
		try {
			ObjectName oname = new ObjectName(onameStr);
			Object value = mBeanServer.getAttribute(oname, att);
			writer.println("OK - Attribute get '" + onameStr + "' - " + att
					+ "= " + escape(value.toString()));
		} catch (Exception ex) {
			writer.println("Error - " + ex.toString());
		}
	}

	public void setAttribute(PrintWriter writer, String onameStr, String att,
			String val) {
		try {
			ObjectName oname = new ObjectName(onameStr);
			String type = MBeanAnnotationRegister.getType(oname, att);
			Object valueObj = MBeanAnnotationRegister.convertValue(type, val);
			mBeanServer.setAttribute(oname, new Attribute(att, valueObj));
			writer.println("OK - Attribute set");
		} catch (Exception ex) {
			writer.println("Error - " + ex.toString());
		}
	}

	public void invokeMethod(PrintWriter writer, String onameStr,
			String methodName) {
		try {
			ObjectName oname = new ObjectName(onameStr);
			Object value = mBeanServer.invoke(oname, methodName,
					new Object[] {}, new String[] {});
			writer.println("OK - method invoke '" + onameStr + "' - "
					+ methodName + "= " + value);
		} catch (Exception ex) {
			writer.println("Error - " + ex.toString());
		}
	}

	public void invokeJsonpMethod(String callback, PrintWriter writer,
			String onameStr, String methodName) {
		try {
			ObjectName oname = new ObjectName(onameStr);
			Object value = mBeanServer.invoke(oname, methodName,
					new Object[] {}, new String[] {});
			String result = callback + "([{ mbeanname:\"" + onameStr
					+ "\",methodname:\"" + methodName + "\",value:\"" + value
					+ "\",ip:\"" + CommonUtils.getIpAddr() + "\"}])";
			writer.println(result);
		} catch (Exception ex) {
			writer.println("Error - " + ex.toString());
		}
	}

	public void listBeans(PrintWriter writer, String qry) {

		Set<ObjectName> names = null;
		try {
			names = mBeanServer.queryNames(new ObjectName(qry), null);
			writer.println("OK - Number of results: " + names.size());
			writer.println();
		} catch (Exception e) {
			writer.println("Error - " + e.toString());
			return;
		}

		Iterator<ObjectName> it = names.iterator();
		while (it.hasNext()) {
			ObjectName oname = (ObjectName) it.next();
			writer.println("[Name]: " + oname.toString());

			try {
				MBeanInfo minfo = mBeanServer.getMBeanInfo(oname);
				String code = minfo.getClassName();
				if ("org.apache.commons.modeler.BaseModelMBean".equals(code)) {
					code = oname
							+ " :: "
							+ (String) mBeanServer.getAttribute(oname,
									"modelerType");
				}
				if ("javax.management.modelmbean.RequiredModelMBean"
						.equals(code)) {
					code = oname + " :: " + minfo.getDescription();
				}
				writer.println("[modelerType]: " + code);

				// ===============属性=================>>>
				MBeanAttributeInfo attrs[] = minfo.getAttributes();
				Object value = null;

				for (int i = 0; i < attrs.length; i++) {
					if (!attrs[i].isReadable())
						continue;
					if (!isSupported(attrs[i].getType()))
						continue;
					String attName = attrs[i].getName();
					if (attName.indexOf("=") >= 0 || attName.indexOf(":") >= 0
							|| attName.indexOf(" ") >= 0) {
						continue;
					}

					try {
						value = mBeanServer.getAttribute(oname, attName);
					} catch (Throwable t) {
						logger.error("Error getting attribute " + oname + " "
								+ attName + " " + t.toString());
						continue;
					}
					if (value == null)
						continue;
					if ("modelerType".equals(attName))
						continue;
					String valueString = value.toString();
					writer.println(attName + ": " + escape(valueString));

				}

				// ===============操作=================>>>
				MBeanOperationInfo[] operations = minfo.getOperations();

				for (int i = 0; i < operations.length; i++) {
					String opName = operations[i].getName();
					writer.println("[operations]: " + opName + " : "
							+ operations[i].getDescription());
				}
			} catch (Exception e) {
				logger.error(e.getMessage(), e);
			}
			writer.println("");
			writer.println("===========================================================>>>>>");
			writer.println("");
		}
	}

	public String escape(String value) {
		int idx = value.indexOf("\n");
		if (idx < 0)
			return value;

		int prev = 0;
		StringBuffer sb = new StringBuffer();
		while (idx >= 0) {
			appendHead(sb, value, prev, idx);

			sb.append("\\n\n ");
			prev = idx + 1;
			if (idx == value.length() - 1)
				break;
			idx = value.indexOf('\n', idx + 1);
		}
		if (prev < value.length())
			appendHead(sb, value, prev, value.length());
		return sb.toString();
	}

	private void appendHead(StringBuffer sb, String value, int start, int end) {
		if (end < 1)
			return;

		int pos = start;
		while (end - pos > 78) {
			sb.append(value.substring(pos, pos + 78));
			sb.append("\n ");
			pos = pos + 78;
		}
		sb.append(value.substring(pos, end));
	}

	public boolean isSupported(String type) {
		return true;
	}

	public void destroy() {

	}

	public void doFilter(ServletRequest request, ServletResponse response,
			FilterChain arg2) throws IOException, ServletException {
		response.setContentType("text/plain");

		PrintWriter writer = response.getWriter();

		if (mBeanServer == null) {
			writer.println("Error - No mbean server");
			return;
		}

		String qry = request.getParameter("set");
		if (qry != null) {
			String name = request.getParameter("att");
			String val = request.getParameter("val");

			setAttribute(writer, qry, name, val);
			return;
		}
		qry = request.getParameter("get");
		if (qry != null) {
			String name = request.getParameter("att");
			getAttribute(writer, qry, name);
			return;
		}

		qry = request.getParameter("opr");
		if (qry != null) {
			String mname = request.getParameter("mname");
			invokeMethod(writer, qry, mname);
			return;
		}

		qry = request.getParameter("jopr");
		String callback = request.getParameter("callback");
		if (qry != null) {
			String mname = request.getParameter("mname");
			invokeJsonpMethod(callback, writer, qry, mname);
			return;
		}

		qry = request.getParameter("qry");
		if (qry == null) {
			qry = "*:*";
		}

		listBeans(writer, qry);
		writer.flush();
		writer.close();
	}

	public void init(FilterConfig arg0) throws ServletException {
		mBeanServer = MBeanAnnotationRegister.getMBeanServer();
	}

}
